#include <stdarg.h>
#include <string.h>
#include "fastlog.h"

#define FASTLOG_DEBUG
#define LOG_BUFF_SIZE (1L << 8)
#define LOG_BUFF_SIZE_MASK (LOG_BUFF_SIZE - 1)

#define LOG_BUFF_FMT_SIZE  (126)
#define LOG_BUFF_DATA_SIZE  (10)
struct fast_log {
    /* 每条日志最多保存 LOG_BUFF_DATA_SIZE 个数字，第 1 个数字用于保存时间戳 */
    unsigned long data[LOG_BUFF_DATA_SIZE];
    /* 每条日志最多保存 LOG_BUFF_FMT_SIZE 个字符 */
    char fmt[LOG_BUFF_FMT_SIZE];

    char prio;
    char logging;
};

static struct fast_log log_buff[LOG_BUFF_SIZE];
static unsigned int head, tail;

#define TIME_STAMP_FMT "<%d> "

#define CHECK_TODO(xxx)\
do {\
    if (buff >= buff_end) goto end;\
    xxx;\
} while (0)

int fastlog(int prio, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    
    int data_count = 0;

    irqstate_t flags = enter_critical_section();
    
    struct fast_log *log = log_buff + head;
    log->logging = 1;
    log->data[data_count++] = get_sys_tick();
    
    head = (head + 1) & LOG_BUFF_SIZE_MASK;
    if (head == tail)
        tail = (tail + 1) & LOG_BUFF_SIZE_MASK;
    
    leave_critical_section(flags);

    log->prio = prio;
    char *buff = log->fmt;
    char *buff_end = log->fmt + LOG_BUFF_FMT_SIZE - 1;
    buff += sizeof(TIME_STAMP_FMT) - 1;

    int fmt_flag = 0;
    
    while (*fmt)
    {
        CHECK_TODO(*buff++ = *fmt);
        
        if (fmt_flag)
        {
            if (*fmt == 's')
            {
                buff -= 2;
                char *ptmp = va_arg(ap, char *);
                while (*ptmp)
                    CHECK_TODO(*buff++ = *ptmp++);
                fmt_flag = 0;
            }
            else if (*fmt == 'd' || *fmt == 'x' || *fmt == 'u' || *fmt == 'p')
            {
                if (data_count >= LOG_BUFF_DATA_SIZE)
                    goto end;
                
                long ln;
                ln = va_arg(ap, long);
                log->data[data_count++] = ln;
                fmt_flag = 0;
            }
        }

        if (*fmt++ == '%')
            fmt_flag = 1;
    }

end:
    va_end(ap);

    *buff++ = 0;
    log->logging = 0;
    return 0;
}

int fastlog_dump(dump_fun pfun, ...)
{
    irqstate_t flags = enter_critical_section();

    while (1)
    {
        if (head == tail || log_buff[tail].logging)
            break;

        unsigned int curr_tail = tail;
        struct fast_log log = log_buff[tail];
        leave_critical_section(flags);

#ifdef FASTLOG_DEBUG
        static unsigned long last_tick = 0;
        if (time_before(log.data[0], last_tick))
        {
            pfun(LOG_EMERG, "log error :%ld, %ld\n", log.data[0], last_tick);
            return -1;
        }
        last_tick = log.data[0];
#endif

        memcpy(log.fmt, TIME_STAMP_FMT, sizeof(TIME_STAMP_FMT) - 1);
        pfun(log.prio, log.fmt, 
            log.data[0], log.data[1], log.data[2], log.data[3], log.data[4],
            log.data[5], log.data[6], log.data[7], log.data[8], log.data[9]);

        flags = enter_critical_section();
        if (tail == curr_tail)
            tail = (curr_tail + 1) & LOG_BUFF_SIZE_MASK;
    }

    leave_critical_section(flags);

    return 0;
}

